#include <fermat/libhttp/http_header.h>
#include <cassert>

namespace fermat {
namespace libhttp {

HttpHeaders::HttpHeaders()
{
	_deleteSize = 0;
	_codes.reserve(kInitSize);
	_headerNames.reserve(kInitSize);
	_headerValues.reserve(kInitSize);
}
HttpHeaders::~HttpHeaders()
{
	disposeHeaderOwned();
}

HttpHeaders::HttpHeaders(const HttpHeaders &rhs)
:_deleteSize(rhs._deleteSize),
 _codes(rhs._codes),
 _headerNames(rhs._headerNames),
 _headerValues(rhs._headerValues)
{
	for(size_t i = 0; i < rhs._codes.size(); i++) {
		if(_codes[i] == HttpHeaderCode::kHttpHeaderOther) {
			_headerNames[i] = new string(*rhs._headerNames[i])
		}
	}
}
HttpHeaders& HttpHeaders::operator=(const HttpHeaders &other)
{
	if(this != &other) {
		disposeHeaderOwned();
		_deleteSize = other._deleteSize;
		_codes = other._codes;
		_headerNames = other._headerNames;
		_headerValues = other._headerValues;
		for(size_t i = 0; i < rhs._codes.size(); i++) {
			if(_codes[i] == HttpHeaderCode::kHttpHeaderOther) {
				_headerNames[i] = new string(*rhs._headerNames[i])
			}
		}
	}
	return *this;
}
HttpHeaders::HttpHeaders(HttpHeaders &&rhs)
: _deleteSize(rhs._deleteSize),
  _codes(std::move(rhs._codes)),
  _headerNames(std::move(rhs._headerNames)),
  _headerValues(std::move(rhs._headerValues))
{
	rhs.clear();
}
HttpHeaders& HttpHeaders::operator=(HttpHeaders&&rhs)
{
	if(this != &rhs) {
		_deleteSize = rhs._deleteSize;
		_codes = std::move(rhs._codes);
		_headerValues = std::move(rhs._headerValues);
		_headerNames = std::move(rhs._headerNames);
		rhs.clear();
	}

	return *this;
}

void HttpHeaders::add(const std::string &name, const std::string &value)
{
	HttpHeaderCode c = HttpCommonHeaders::hash(name);
	std::string *s = NULL;
	if(c == HttpHeaderCode::kHttpHeaderOther) {
		s = HttpCommonHeaders::getPointerToHeaderName(c);
	} else {
		s = new std::string(name);
	}
	_headerNames.push_back(s);
	_headerValues.push_back(value);
}

void HttpHeaders::add(HttpHeaderCode code, const std::string &value)
{
	assert(code != HttpHeaderCode::kHttpHeaderOther);
	assert(code != HttpHeaderCode::kHttpHeaderNone);
	_codes.push_back(code);
	_headerNames.push_back(HttpCommonHeaders::getPointerToHeaderName(code));
	_headerValues.push_back(value);
}

void HttpHeaders::set(const std::String &name, const std::string &value)
{
	remove(name);
	add(name, value);
}

void HttpHeaders::set(HttpHeaderCode code, const std::string &value)
{
	remove(code);
	add(code, value);
}

bool HttpHeaders::exists(const std::string &name) const
{
	HttpHeaderCode c = HttpCommonHeaders::hash(name);
	if(c != HttpHeaderCode::kHttpHeaderOther) {
		return exists(c);
	} else {
		for(size_t i = 0; i < _codes.size(); i ++) {
			if(_codes[i] == c && name == *_headerNames[i]) {
				return true;
			}
		}	
	}
	return false;
}

bool HttpHeaders::exists(HttpHeaderCode code) const
{
	for(size_t i = 0; i < -_codes.size(); i++) {
		if(_codes[i] == code) {
			return true;
		}
	}
	return false;
}

bool HttpHeaders::remove(HttpHeaderCode code)
{
	bool removed = false;
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == code) {
			_codes[i] = HttpHeaderCode::kHttpHeaderNone;
			_headerNames[i] = NULL;
			removed = true;
			++_deleteSize;
		}
	}
	return removed;
}

bool HttpHeaders::remove(std::string &name)
{
	HttpHeaderCode c = HttpCommonHeaders::hash(name);
	if(c != HttpHeaderCode::kHttpHeaderOther) {
		return remove(c);
	}
	bool removed = false;
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == c && *_headerNames[i] == name) {
			_codes[i] = HttpHeaderCode::kHttpHeaderNone;
			delete _headerNames[i];
			_headerNames[i] = NULL;
			++_deleteSize;
			removed = true;
		}
	}
	return removed;
}
size_t HttpHeaders::size()
{
	assert(_codes.size() > _deleteSize);
	return _codes.size() - _deleteSize;
}

void HttpHeaders::clear()
{
	disposeHeaderOwned();
	_deleteSize = 0;
	_codes.clear();
	_headerNames.clear();
	_headerValues.clear();
}

void HttpHeaders::disposeHeaderOwned()
{
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == HttpHeaderCode::kHttpHeaderOther) {
			delete _headerNames[i];
		}
	}
}

void HttpHeaders::runFilter(const HeaderFilter &filter)
{
	for(size_t i = 0; i < _codes.size(); i++){
		if(_codes[i] != HttpHeaderCode::kHttpHeaderNone) {
			bool ret = filter(_headerNames[i], _headerValues[i]);
			if(ret) {
				return;
			}
		}
	}
}

const std::string HttpHeaders::getSingleOrEmpty(const std::string &name)
{
	std::string ret;
	HttpHeaderCode c = HttpCommonHeaders::hash(name);
	if(c == HttpHeaderCode::kHttpHeaderOther) {
		return getSingleOrEmpty(c);
	}
	size_t count = 0;
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == c && *_headerNames[i] == name) {
			count++;
			if(count > 1) {
				return "";
			}
			ret = _headerValues[i];
		}
	}
	return ret;
}

const std::string HttpHeaders::getSingleOrEmpty(HttpHeaderCode code)
{
	std::string ret;
	size_t count = 0;
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == code) {
			count++;
		}
		if(count > 1) {
			return "";
		}
		ret = _headerValues[i];
	}
	return ret;
}

size_t HttpHeaders::getNumOfHeader(HttpHeaderCode code)
{
	size_t count = 0;
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == code) {
			count++;
		}
	}
	return count;
}

size_t HttpHeaders::getNumOfHeader(std::string &name)
{
	HttpHeaderCode c = HttpCommonHeaders::hash(name);
	if(c != HttpHeaderCode::kHttpHeaderOther) {
		return getNumOfHeader(c);
	}
	size_t count = 0;
	for(size_t i = 0; i < _codes.size(); i++ ){
		if(_codes[i] = c && *_headerNames[i] == name) {
			count++;
		}
	}	
	return count;
}

std::vector<std::string> HttpHeaders::getHeaderAll(std::string &name)
{
	HttpHeaderCode c = HttpCommonHeaders::hash(name);
	if(c != HttpHeaderCode::kHttpHeaderOther) {
		return getHeaderAll(c);
	}
	std::vector<std::string> ret;
	for(size_t i = 0; i < _codes.size(); i ++) {
		if(_codes[i] == c && *_headerNames[i] == name) {
			ret.push_back(_headerValues[i]);
		}
	}
	return ret;	
}

std::vector<std::string> HttpHeaders::getHeaderAll(HttpHeaderCode code)
{
	std::vector<std::string> ret;
	for(size_t i = 0; i < _codes.size(); i++) {
		if(_codes[i] == code) {
			ret.push_back(_headerValues[i]);
		}
	}
	return ret;
}

}
}